package dkbta.ontology.elements.pattern;

import java.util.ArrayList;
import java.util.List;

import dkbta.ontology.Ontology;
import dkbta.ontology.Validatable;
import dkbta.ontology.elements.Element;
import dkbta.ontology.elements.ElementDef;
import dkbta.ontology.elements.ElementType;
import dkbta.ontology.elements.pattern.conditions.BoundPointCondition;
import dkbta.ontology.elements.pattern.conditions.DurationCondition;
import dkbta.ontology.loading.OntologyException;
import dkbta.ontology.values.ValueCondition;
import dkbta.processing.Patient;

/**
 * A pattern element
 * 
 * @author Uri Kanonov
 */
public class PatternElement implements Validatable{
	/**
	 * The name of the pattern element, only used during the parsing and is kept for
	 * to-string purposes. After parsing it is converted to an ordinal
	 */
	protected final String _patternElementName;

	/** The ordinal of the pattern element in the pattern */
	private final int _ordinal;

	/** The name of the corresponding ontology element */
	protected final String _ontologyElementName;

	/** The type of the corresponding ontology element */
	private final ElementType _ontologyElementType;

	/** The value condition on the ontology element */
	private final ValueCondition _valueCondition;

	/** The duration condition on the ontology element */
	private final DurationCondition _durationCondition;

	/** The bound-point condition on the ontology element */
	private final BoundPointCondition _boundPointCondition;

	/**
	 * @param patternElementName The name of the pattern element, only used during the
	 *        parsing and is kept for to-string purposes
	 * @param patternElementOrdinal The ordinal of the pattern element in the pattern
	 * @param ontologyElementName The name of the corresponding ontology element
	 * @param ontologyElementType The type of the corresponding ontology element
	 * @param valueCondition The value condition on the ontology element
	 * @param durationCondition The duration condition on the ontology element
	 * @param boundPointCondition The bound-point condition on the ontology element
	 */
	public PatternElement(String patternElementName, int patternElementOrdinal,
		String ontologyElementName, ElementType ontologyElementType,
		ValueCondition valueCondition, DurationCondition durationCondition,
		BoundPointCondition boundPointCondition){
		_patternElementName = patternElementName;
		_ordinal = patternElementOrdinal;
		_ontologyElementName = ontologyElementName;
		_valueCondition = valueCondition;
		_durationCondition = durationCondition;
		_boundPointCondition = boundPointCondition;
		_ontologyElementType = ontologyElementType;
	}

	/**
	 * Returns the ordinal of the pattern element in the pattern
	 * 
	 * @return The ordinal of the pattern element in the pattern
	 */
	public int getOrdinal(){
		return _ordinal;
	}

	@Override
	public void validate(Ontology ontology) throws OntologyException{
		if (_valueCondition != null){
			_valueCondition.validate(ontology);
		}
		ElementDef ed = getElementDef(ontology);
		if (ed == null){
			throw new OntologyException("The pattern element " + _patternElementName
					+ " refers to a non-existing " + _ontologyElementType + ": "
					+ _ontologyElementName);
		}
	}

	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("\r\n\t#").append(_ordinal).append(" [").append(_patternElementName)
				.append("]");
		sb.append("\r\n\t\tOntology Element: ").append(_ontologyElementName);
		sb.append("\r\n\t\tValue Condition: ").append(_valueCondition);
		sb.append("\r\n\t\tDuration Condition: ").append(_durationCondition);
		sb.append("\r\n\t\tBoundpoint Condition: ").append(_boundPointCondition);
		sb.append("\r\n");
		return sb.toString();
	}

	/**
	 * Returns the list of instances of the corresponding ontology element from the given
	 * patient after filtering according to each of the value, duration and bound-point
	 * conditions
	 * 
	 * @param p The patient from which to take the instances
	 * @return The filtered list of instances
	 */
	public List<Element> getValidElements(Patient p){
		List<? extends Element> allElements = p.getElements(_ontologyElementType,
			_ontologyElementName);
		List<Element> validElements = new ArrayList<Element>();

		for (Element e : allElements){
			if ((_valueCondition == null || _valueCondition.checkCondition(e))
					&& (_durationCondition == null || _durationCondition
							.checkCondition(e))
					&& (_boundPointCondition == null || _boundPointCondition
							.checkCondition(e))){
				// The element matches all existing conditions so it's valid
				validElements.add(e);
			}
		}

		return validElements;
	}

	/**
	 * Returns the element definition of the associated ontology element
	 * 
	 * @param ontology A reference to the ontology
	 * @return The element definition of the associated ontology element
	 */
	public ElementDef getElementDef(Ontology ontology){
		return ontology.getElement(_ontologyElementType, _ontologyElementName);
	}

}
